package com.tenebrous.jestic.server;

import com.rain.ops.sdk.utils.DateUtils;
import com.tenebrous.jestic.auth.AuthTokenGenerator;
import com.tenebrous.jestic.compents.exception.InstanceException;
import com.tenebrous.jestic.entity.server.JesticServer;
import com.tenebrous.jestic.enums.InstanceStatusEnum;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description:
 * @Project: jestic-server
 * @Package: com.tenebrous.jestic.server
 * @Author: zhangya
 * @CreateTime: 2023-06-03  13:50
 * @Version: 1.0.0
 */
@Component
public class AbstractInstanceContext implements InstanceContext, EditableInstanceContext {

    private JesticServer jesticServer;

    private AuthTokenGenerator authTokenGenerator;

    /**按服务名称分类**/
    private final Map<String, List<Instance>> instanceMap = new ConcurrentHashMap<>(64);

    /**所有实例列表**/
    private final List<Instance> instances = new ArrayList<>();

    /**按环境分类**/
    private final Map<String, List<Instance>> instancesEnvMap = new ConcurrentHashMap<>(64);

    public AbstractInstanceContext() {

    }

    public AbstractInstanceContext initContext(JesticServer jesticServer, AuthTokenGenerator authTokenGenerator) {
        this.jesticServer = jesticServer;
        this.authTokenGenerator = authTokenGenerator;
        return this;
    }

    public AbstractInstanceContext initContext(AuthTokenGenerator authTokenGenerator) {
        this.authTokenGenerator = authTokenGenerator;
        return this;
    }

    @Override
    public List<Instance> getInstance() {
        return instances;
    }

    @Override
    public List<Instance> getInstance(String serverName) {
        return instanceMap.get(serverName);
    }

    @Override
    public List<Instance> getInstanceByEnv(String env) {
        return instancesEnvMap.get(env);
    }

    @Override
    public Instance getInstance(String serverName, String instanceId) {
        if (StringUtils.isEmpty(serverName) || StringUtils.isEmpty(instanceId)) {
            return null;
        }
        List<Instance> instanceList = instanceMap.get(serverName);
        return instanceList
                .stream().filter(instance->instance.getInstanceId().equals(instanceId))
                .findFirst()
                .orElse(null);
    }

    /**
     * @description:
     * 根据token获取实例
     * 该实现会实时计算token有效期
     * 当token过期后，会将实例暂停访问，并给客户端提示token过期
     * 当token将在一小时内过期时，自动续期24小时
     *
     * @author: zhangya
     * @date: 2023/6/4 9:34
     * @param:
     * @return:
     **/
    @Override
    public Instance getInstanceByToken(String token) {
        if (StringUtils.isBlank(token)) {
            return null;
        }
        // 获取token
        List<Instance> instances = getInstance();
        Instance instance = instances
                .stream()
                .filter(its -> its.getToken().equals(token)
                        && its.getInstanceStatus().equals(InstanceStatusEnum.HEALTHY))
                .findFirst()
                .orElse(null);
        // 校验实例是否过期
        long time = instance.getExpireTime() - new Date().getTime();
        if (time >= 0) {
            // 未过期 判断是否相差是否超过一小时
            if (time <= 3600000) {
                // 自动续期
                instance.setExpireTime(instance.getExpireTime() + 82800000);
                // 刷新回map中
                flushInstance(instance);
            }
            return instance;
        }
        // 更改实例状态，过期时间修改为0，token置为空
        instance.setInstanceStatus(InstanceStatusEnum.UNAVAILABLE);
        instance.setExpireTime(0L);
        instance.setToken("");
        // 刷新回map中
        flushInstance(instance);
        return null;
    }

    /**
     * @description:
     * 移除实例
     *
     * @author: zhangya
     * @date: 2023/6/4 16:36
     * @param:
     * @return:
     **/
    @Override
    public void removeInstance(Instance instance) {
        // 1、移除 instanceMap 按服务分类
        removeInstanceMap(instance);
        // 2、移除 instances 所有服务
        removeInstances(instance);
        // 3、移除 instancesEnvMap 按环境分类
        removeInstanceEnvMap(instance);
    }

    @Override
    public Instance getInstanceBySign(String serverSign) {
        return getInstance().stream().filter(instance -> instance.getServerSign().equals(serverSign)).findFirst().orElse(null);
    }

    @Override
    public Instance getInstanceById(String id) {
        if (StringUtils.isEmpty(id)) {
            return null;
        }
        return instances.stream().filter(instance -> instance.getInstanceId().equals(id)).findFirst().orElse(null);
    }

    private void removeInstanceMap(Instance instance) {
        synchronized (instanceMap) {
            List<Instance> instances = instanceMap.get(instance.getServerName());
            Instance oldInstance = instances.stream().filter(x -> x.getInstanceId().equals(instance.getInstanceId())).findFirst().orElse(null);
            instances.remove(oldInstance);
        }
    }

    private void removeInstances(Instance instance) {
        synchronized (instances) {
            Instance oldInstance = instances.stream().filter(x -> x.getInstanceId().equals(instance.getInstanceId())).findFirst().orElse(null);
            instances.remove(oldInstance);
        }
    }

    private void removeInstanceEnvMap(Instance instance) {
        synchronized (instancesEnvMap) {
            List<Instance> instances = instancesEnvMap.get(instance.getEnv());
            Instance oldInstance = instances.stream().filter(x -> x.getInstanceId().equals(instance.getInstanceId())).findFirst().orElse(null);
            instances.remove(oldInstance);
        }
    }

    /**
     * @description:
     * 刷新实例
     *
     * @author: zhangya
     * @date: 2023/6/4 9:58
     * @param:
     * @return:
     **/
    @Override
    public void flushInstance(Instance instance) {
        // 1、刷新到 instanceMap 按服务分类
        flushInstanceMap(instance);
        // 2、刷新到 instances 所有服务
        flushInstances(instance);
        // 3、刷新到 instancesEnvMap 按环境分类
        flushInstanceEnvMap(instance);
    }

    /**
     * @description:
     * 刷新到 instancesEnvMap 按服务分类
     *
     * @author: zhangya
     * @date: 2023/6/4 10:03
     * @param:
     * @return:
     **/
    public void flushInstanceEnvMap(Instance instance) {
        synchronized (instancesEnvMap) {
            List<Instance> instances = instancesEnvMap.get(instance.getEnv());
            if (CollectionUtils.isEmpty(instances)) {
                instances = new ArrayList<>();
            }
            // 根据实例id查出旧的实例信息
            Instance oldInstance = instances.stream().filter(its -> its.getInstanceId().equals(instance.getInstanceId())).findFirst().orElse(null);
            if (!Objects.isNull(oldInstance)) {
                instances.remove(oldInstance);
            }
            instances.add(instance);
            instancesEnvMap.put(instance.getEnv(), instances);
        }
    }

    @Override
    public Instance resettingInstance(Instance instance) {
        instance.setExpireTime(DateUtils.addMinutes(60 * 24).getTime());
        instance.setRecentlyAuth(new Date().getTime());
        instance.setInstanceStatus(InstanceStatusEnum.HEALTHY);
        instance.setToken(getAuthTokenGenerator().generateToken(instance.getInstanceId()));
        return instance;
    }

    public void flushInstances(Instance instance) {
        synchronized (instances) {
            Instance oldInstance = instances.stream().filter(its -> its.getInstanceId().equals(instance.getInstanceId())).findFirst().orElse(null);
            if (!Objects.isNull(oldInstance)) {
                instances.remove(oldInstance);
            }
            instances.add(instance);
        }
    }

    @Override
    public void flushInstanceMap(Instance instance) {
        synchronized (instanceMap) {
            List<Instance> instances = instanceMap.get(instance.getEnv());
            if (CollectionUtils.isEmpty(instances)) {
                instances = new ArrayList<>();
            }
            // 根据实例id查出旧的实例信息
            Instance oldInstance = instances.stream().filter(its -> its.getInstanceId().equals(instance.getInstanceId())).findFirst().orElse(null);
            if (!Objects.isNull(oldInstance)) {
                instances.remove(oldInstance);
            }
            instances.add(instance);
            instanceMap.put(instance.getServerName(), instances);
        }
    }

    public JesticServer getJesticServer() {
        return jesticServer;
    }

    public AuthTokenGenerator getAuthTokenGenerator() {
        return authTokenGenerator;
    }

    /**
     * @description:
     * 注册实例信息
     *
     * @author: zhangya
     * @date: 2023/6/3 23:05
     * @param:
     * @return:
     **/
    @Override
    public void registerInstance(Instance instance) {
        if (Objects.isNull(instance)) {
            throw new InstanceException("jestic register instance error, instance not found");
        }
        // 1、注册 instanceMap 按服务分类
        registerInstanceMap(instance);
        // 2、注册 instances 所有服务
        registerinstances(instance);
        // 3、注册 instancesEnvMap 按环境分类
        registerInstanceEnvMap(instance);
    }

    private void registerInstanceEnvMap(Instance instance) {
        List<Instance> instances = instancesEnvMap.get(instance.getEnv());
        if (CollectionUtils.isEmpty(instances)) {
            instances = new ArrayList<>();
        }
        Instance instanceSign = instances.stream()
                .filter(its -> its.getServerSign().equals(instance.getServerSign()))
                .findFirst()
                .orElse(null);
        if (!Objects.isNull(instanceSign)) {
            return;
        }
        instances.add(instance);
        instancesEnvMap.put(instance.getEnv(), instances);
    }

    private void registerinstances(Instance instance) {
        Instance instanceSign = instances.stream()
                .filter(its -> its.getServerSign().equals(instance.getServerSign()))
                .findFirst()
                .orElse(null);
        if (!Objects.isNull(instanceSign)) {
            return;
        }
        instances.add(instance);
    }

    private void registerInstanceMap(Instance instance) {
        List<Instance> instances = instanceMap.get(instance.getServerName());
        if (CollectionUtils.isEmpty(instances)) {
            instances = new ArrayList<>();
        }
        Instance instanceSign = instances.stream()
                .filter(its -> its.getServerSign().equals(instance.getServerSign()))
                .findFirst()
                .orElse(null);
        if (!Objects.isNull(instanceSign)) {
            return;
        }
        instances.add(instance);
        instanceMap.put(instance.getServerName(), instances);
    }
}
